package com.studerb.model;

import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.glowacki.CalendarParser;
import org.glowacki.CalendarParserException;

public class Header extends Post {
	private final static Logger logger = Logger.getLogger(Header.class);

	protected String messageId;
	protected Integer lineCount;
	protected Integer currentPart;
	protected HeaderThread headerThread;

	public Header() {}

	/**
	 * According to RFC 2980 'the sequence of fields must be in this order:
	 * subject, author, date, message-id, references, byte count, and line
	 * count.'
	 * 
	 * @param line
	 *            String containing the reply of a NNTP XOVER command
	 * @return Header set with the appropriate header properties
	 */
	public static Header createHeader(String line, NewsGroup newsGroup) {
		Header header = new Header();
		String[] splitLines = null;
		try {
			splitLines = line.split("\t");
			// logger.debug(String.format("SplitLine: %d fields split by tab",
			// splitLine.length));
			if (splitLines.length < 8) {
				logger.warn("Bad xover line: " + line);
				return null;
			}
			header.setArticleNumber(Long.parseLong(splitLines[0]));
			header.setSubject(splitLines[1]);
			header.setAuthor(splitLines[2]);
			header.setDatePosted(xoverParseDate(splitLines[3]));
			header.setMessageId(splitLines[4]);
			// We ignore splitLines[7] as it is 'references'
			header.setByteCount(Long.parseLong(splitLines[6]));
			header.setLineCount(Integer.parseInt(splitLines[7]));
			header.setNewsGroup(newsGroup);
		}
		catch (Exception e) {
			logger.warn(String.format("Error creating header: %s - EXCEPTION MESSAGE: %s", line, ExceptionUtils.getRootCauseMessage(e)));
			for (String s : splitLines) {
				logger.warn("\t" + s);
			}
			return null;
		}

		if (!validateHeader(header)) {
			logger.warn("Bad Header: :Did not validate");
			for (String s : splitLines) {
				logger.warn("\t" + s);
			}
			return null;
		}
		return header;
	}

	public static boolean validateHeader(Header header) {
		if (header.getAuthor().length() > AUTHOR_LENGTH) {
			return false;
		}
		if (header.subject.length() > SUBJECT_LENGTH) {
			return false;
		}
		if (header.messageId.length() > MESSAGE_ID_LENGTH) {
			return false;
		}
		if (header.fileName != null && header.fileName.length() > Post.FILE_NAME_LENGTH) {
			return false;
		}
		return true;
	}

	/**
	 * We are parsing a date using CalendarParser, though we must check for
	 * explicit (+-xxxx e.g. +0200) timezone offsets at the end of date inputs
	 * as the CalendarParser class chokes on these sometimes.
	 * 
	 * @param dateLine
	 *            Date Header from xover command
	 * @return Calendar representing parsed date
	 * @throws CalendarParserException
	 *             if unable to parse date
	 */
	private static Calendar xoverParseDate(String dateLine) throws CalendarParserException {
		Matcher matcher1 = Pattern.compile("(.*)([+-]\\d{3,4})( \\(\\w{3,4}\\))$").matcher(dateLine);
		if (matcher1.matches()) {
			dateLine = StringUtils.removeEnd(dateLine, matcher1.group(3));
		}
		Matcher matcher = Pattern.compile("[+-]\\d{3,4}$").matcher(dateLine);
		if (matcher.find()) {
			String removedOffset = dateLine.substring(0, matcher.start());
			String offset = dateLine.substring(matcher.start());
			String hours, minutes;
			if (offset.length() == 4) {
				hours = offset.substring(0, 2);
				minutes = offset.substring(2, 4);
			}
			else {
				hours = offset.substring(0, 3);
				minutes = offset.substring(3, 5);
			}
			String newToken = hours + ":" + minutes;
			dateLine = removedOffset + newToken;
		}
		return CalendarParser.parse(dateLine);
	}

	public Integer getCurrentPart() {
		return currentPart;
	}

	public HeaderThread getHeaderThread() {
		return headerThread;
	}

	public Integer getLineCount() {
		return lineCount;
	}

	public String getMessageId() {
		return messageId;
	}

	public void setCurrentPart(Integer currentPart) {
		this.currentPart = currentPart;
	}

	public void setHeaderThread(HeaderThread headerThread) {
		this.headerThread = headerThread;
	}

	public void setLineCount(Integer lineCount) {
		this.lineCount = lineCount;
	}

	public void setMessageId(String messaegeId) {
		messageId = messaegeId;
	}

	@Override public String toString() {
		return "Group / ArticleNumber / CurrentPart " + this.getNewsGroup().getGroupName() + " / " + this.getArticleNumber() + " / " + this.getCurrentPart();
	}

}
